package thesis.filesharing.web.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import thesis.filesharing.bo.SubjectBO;
import thesis.filesharing.common.util.Utils;
import thesis.filesharing.model.Subject;
import thesis.filesharing.resources.Constants;
import thesis.filesharing.services.fulltextsearch.indexing.DocumentIndexing;
import thesis.filesharing.services.fulltextsearch.indexing.DocumentQuery;
import thesis.filesharing.services.fulltextsearch.indexing.Operators;
import thesis.filesharing.services.fulltextsearch.indexing.QueryBuilder;
import thesis.filesharing.web.util.PageInfo;
import thesis.filesharing.web.util.PropertiesUtil;
import thesis.filesharing.web.util.Util;
import thesis.filesharing.web.validator.CreateSubjectValidator;
import thesis.filesharing.web.validator.UpdateSubjectValidator;
import thesis.filesharing.web.viewmodel.CreateSubjectViewModel;
import thesis.filesharing.web.viewmodel.UpdateSubjectViewModel;
import thesis.filesharing.web.viewmodel.ajaxbeans.SubjectBean;

import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.search.Field;
import com.google.appengine.api.search.Results;
import com.google.appengine.api.search.ScoredDocument;

@Controller
public class SubjectController {
	
	@Autowired
	private CreateSubjectValidator createSubjectValidator;
	
	@Autowired
	private UpdateSubjectValidator updateSubjectValidator;
	
	@Autowired
	private SubjectBO subjectBO;
	
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@RequestMapping(value = "/admin/listSubjects", method = RequestMethod.GET)
	public String listSubjects(@RequestParam(value = "p", required = false, defaultValue = "1") Integer p, ModelMap model) {
		if (p < 1 || p > Util.calculateNumberOfPage(subjectBO.countSubjects(), Constants.PAGE_SIZE)) {
			p = 1;
		}
		
		model.addAttribute("subjects", subjectBO.listSubjects(p, Constants.PAGE_SIZE));
		model.addAttribute("pageInfo", new PageInfo(subjectBO.countSubjects(), p));
		model.addAttribute("pageSize", Constants.PAGE_SIZE);
		
		return "admin/listSubjects";
	}
	
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@RequestMapping(value = "/admin/createSubject", method = RequestMethod.GET)
	public String initCreateSubjectForm(ModelMap model) {
		CreateSubjectViewModel createSubjectViewModel = new CreateSubjectViewModel();
		model.addAttribute("createSubjectViewModel", createSubjectViewModel);
		return "admin/createSubject";
	}
	
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@RequestMapping(value = "/admin/createSubject", method = RequestMethod.POST)
	public ModelAndView processCreateSubjectSubmit(
			@ModelAttribute("createSubjectViewModel") CreateSubjectViewModel createSubjectViewModel,
			BindingResult bindingResult, SessionStatus status, ModelMap model, HttpSession session) {
		
		createSubjectValidator.validate(createSubjectViewModel, bindingResult);
		
		if (bindingResult.hasErrors()) {
			return new ModelAndView("admin/createSubject");
		} else {
			status.setComplete();
			
			Subject subject = subjectBO.saveSubject(new Subject(createSubjectViewModel.getSubjectId(), createSubjectViewModel.getSubjectName()));
			DocumentIndexing.addEntityIndex(subject, Constants.SEARCH_INDEX_NAME);
			
			session.setAttribute("msg", PropertiesUtil.getProperty("subject.create.success"));
			return new ModelAndView(new RedirectView("/admin/listSubjects.html"));
		}
	}
	
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@RequestMapping(value = "/admin/deleteSubject/{key}", method = RequestMethod.GET)
	public RedirectView deleteSubject(@PathVariable("key") String key, 
			@RequestParam(value = "p", required = false, defaultValue = "1") Integer p,
			ModelMap model, HttpSession session, HttpServletRequest request) {
		if (p < 1 || p > Util.calculateNumberOfPage(subjectBO.countSubjects(), Constants.PAGE_SIZE)) {
			p = 1;
		}
		
		int totalItems = 0;
		int newPage = 1;
		totalItems = subjectBO.countSubjects();
		subjectBO.deleteSubject(key);
		DocumentIndexing.removeEntityIndex(key, Constants.SEARCH_INDEX_NAME);
		session.setAttribute("msg", PropertiesUtil.getProperty("subject.delete.success"));
		newPage = Util.evaluatePageNumAfterDelete(p, totalItems, Constants.PAGE_SIZE, 1);
		
		return new RedirectView("/admin/listSubjects.html?p=" + newPage);	
	}
	
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@RequestMapping(value = "/admin/deleteSubjects", method = RequestMethod.POST)
	public RedirectView deleteSubjects(@RequestParam(value = "p", required = false, defaultValue = "1") Integer p,
			ModelMap model, HttpServletRequest request, HttpSession session) {
		if (p < 1 || p > Util.calculateNumberOfPage(subjectBO.countSubjects(), Constants.PAGE_SIZE)) {
			p = 1;
		}
		
		String[] subjectKeys = request.getParameterValues("checkItem");
		int totalItems = 0;
		int newPage = 1;
		
		totalItems = subjectBO.countSubjects();
		
		if (subjectKeys != null && subjectKeys.length > 0) {
			for (String k : subjectKeys) {
				subjectBO.deleteSubject(k);
			}
			DocumentIndexing.removeEntityIndexes(new ArrayList<String>(Arrays.asList(subjectKeys)), Constants.SEARCH_INDEX_NAME);
			newPage = Util.evaluatePageNumAfterDelete(p, totalItems, Constants.PAGE_SIZE, subjectKeys.length);
		}
		session.setAttribute("msg", PropertiesUtil.getProperty("subject.delete.success"));
		
		return new RedirectView("/admin/listSubjects.html?p=" + newPage);
	}
	
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@RequestMapping(value = "/admin/updateSubject/{key}", method = RequestMethod.GET)
	public String initUpdateSubjectForm(@PathVariable("key") String key, ModelMap model) {
		UpdateSubjectViewModel updateSubjectViewModel = new UpdateSubjectViewModel();
		Subject s = subjectBO.findSubjectByKey(key);
		if (s != null) {
			updateSubjectViewModel.setSubjectKey(KeyFactory.keyToString(s.getSubjectKey()));
			updateSubjectViewModel.setSubjectId(s.getSubjectId());
			updateSubjectViewModel.setSubjectName(s.getSubjectName());
		}
		model.addAttribute("updateSubjectViewModel", updateSubjectViewModel);
		return "admin/updateSubject";
	}
	
	@PreAuthorize("hasRole('ROLE_ADMIN')")
	@RequestMapping(value = "/admin/updateSubject/{key}", method = RequestMethod.POST)
	public ModelAndView processUpdateSubjectSubmit(
			@ModelAttribute("updateSubjectViewModel") UpdateSubjectViewModel updateSubjectViewModel,
			@RequestParam(value = "p", required = false, defaultValue = "1") Integer p,
			BindingResult bindingResult, SessionStatus status, ModelMap model, HttpSession session, HttpServletRequest request) {
		
		updateSubjectValidator.validate(updateSubjectViewModel, bindingResult);
		
		if (bindingResult.hasErrors()) {
			return new ModelAndView("admin/updateSubject");
		} else {
			status.setComplete();
			
			Subject subject = new Subject(KeyFactory.stringToKey(updateSubjectViewModel.getSubjectKey()), 
					updateSubjectViewModel.getSubjectId(), updateSubjectViewModel.getSubjectName(), null);
			subject = subjectBO.updateSubject(subject);
			DocumentIndexing.addEntityIndex(subject, Constants.SEARCH_INDEX_NAME);
			
			session.setAttribute("msg", PropertiesUtil.getProperty("subject.update.success"));
			return new ModelAndView(new RedirectView("/admin/listSubjects.html?p=" + p));
		}
	}
	
	@PreAuthorize("hasAnyRole('ROLE_ADMIN', 'ROLE_LECTURER', 'ROLE_STUDENT')")
	@RequestMapping(value = "/searchSubjectInfoAjax", method = RequestMethod.GET)
	public void searchSubjectInfoAjax(@RequestParam("q") String q, HttpServletResponse response) throws IOException {
		response.setContentType("application/json");
		PrintWriter out = response.getWriter();
		
		q = Utils.insertSignChar(Utils.filterVietnameseSign(q));
		if (q.trim().length() > 0) {
			QueryBuilder builder = new QueryBuilder();
			String query = builder.add("fSubjectId", q, Operators.COLON)
					.or("fSubjectName", q, Operators.COLON)
					.buildQuery();
			
			Results<ScoredDocument> results = DocumentQuery.search(query, Constants.SEARCH_INDEX_NAME);
			List<SubjectBean> subjectBeans = new ArrayList<SubjectBean>();
			
			for (ScoredDocument result : results) {
				Iterator<Field> fields = result.getFields().iterator();
				
				Field f = null;
				String fieldName = null;
				SubjectBean subjectBean = new SubjectBean();
				subjectBean.setSubjectKey(result.getId());
				
				while (fields.hasNext()) {
					f = fields.next();
					fieldName = f.getName();
					
					if ("subjectId".equals(fieldName)) {
						subjectBean.setSubjectId(f.getText());
					} else if ("subjectName".equals(fieldName)) {
						subjectBean.setSubjectName(f.getText());
					}
				}
				
				subjectBeans.add(subjectBean);
			}
			
			ObjectMapper mapper = new ObjectMapper();
			out.print(mapper.writeValueAsString(subjectBeans));
		} else {
			out.print("[]");
		}
	}
}
